package com.douxg.learning.springboot.base.util;

import java.math.BigDecimal;
import java.text.DecimalFormat;

public class UnitConvertUtils {
    private static final String[] DECIMALFORMATPATTERNS = {"#", "#.#", "#.##", "#.###"};

    public static String convertMemory(Long value) {
        if (value == null) {
            return 0 + MemoryUnit.b.getUnit();
        }
        return convert(value.doubleValue(), MemoryUnit.b, 2);
    }

    /**
     * 根据指定单位的值，转换为所能转换的最大单位的值，比如传入1024，单位为b，返回结果为1kb
     *
     * @param value     值
     * @param precision 精确度，保留的小数位数，比如2表示结果保留两位小数
     * @return 返回结果带单位
     */
    public static String convertMemory(Long value, int precision) {
        if (value == null) {
            return 0 + MemoryUnit.b.getUnit();
        }
        return convert(value.doubleValue(), MemoryUnit.b, precision);
    }

    public static String convertDate(Long value) {
        if (value == null) {
            return 0 + DateUnit.ms.getUnit();
        }
        return convert(value.doubleValue(), DateUnit.ms, 2);
    }

    /**
     * 根据指定单位的值，转换为所能转换的最大单位的值，比如传入1024，单位为b，返回结果为1kb
     *
     * @param value     值
     * @param precision 精确度，保留的小数位数，比如2表示结果保留两位小数
     * @return 返回结果带单位
     */
    public static String convertDate(Long value, int precision) {
        if (value == null) {
            return 0 + DateUnit.ms.getUnit();
        }
        return convert(value.doubleValue(), DateUnit.ms, precision, null);
    }

    public static String convertDate(Long value, int precision, unit appointUnit) {
        if (value == null) {
            return 0 + DateUnit.ms.getUnit();
        }
        return convert(value.doubleValue(), DateUnit.ms, precision, appointUnit);
    }

    public static String convert(Double value, unit unitNum, int precision) {
        return convert(value, unitNum, precision, null);
    }

    /**
     * 根据指定单位的值，转换为所能转换的最大单位的值，比如传入1024，单位为b，返回结果为1kb
     *
     * @param value     值
     * @param unitNum   单位类型
     * @param precision 精确度，保留的小数位数，比如2表示结果保留两位小数
     * @return 返回结果带单位
     */
    public static String convert(Double value, unit unitNum, int precision, unit appointUnit) {
        if (value == null || value <= 0) {
            return 0 + unitNum.getUnit();
        }
        if (value < unitNum.getScale() || unitNum.getSupper() == null || unitNum == appointUnit) {
            String valueStr = new DecimalFormat(createDecimalFormatPattern(precision)).format(value);
            return new StringBuilder(valueStr).append(unitNum.getUnit()).toString();
        }
        String str = convert(new BigDecimal(String.valueOf(value)).divide(new BigDecimal(unitNum.getScale()), precision, BigDecimal.ROUND_HALF_EVEN).doubleValue(), unitNum.getSupper(), precision, appointUnit);
        return str;
    }

    public interface unit {
        public String getUnit();

        public unit getSupper();

        public int getScale();
    }

    public enum MemoryUnit implements unit {
        e("EB", null),
        p("PB", e),
        t("TB", p),
        g("GB", t),
        m("MB", g),
        k("KB", m),
        b("B", k);
        private String unit;
        private MemoryUnit supper;
        //跟上级单位转换的比率，比如B->k，需要除以1024，则B的scale=1024
        public static final int memoryScale = 1024;

        MemoryUnit(String unit, MemoryUnit supper) {
            this.unit = unit;
            this.supper = supper;
        }

        @Override
        public String getUnit() {
            return unit;
        }

        @Override
        public MemoryUnit getSupper() {
            return supper;
        }

        @Override
        public int getScale() {
            return memoryScale;
        }
    }

    public enum DateUnit implements unit {
        d("d", null, 1),
        h("h", d, 24),
        m("m", h, 60),
        s("s", m, 60),
        ms("ms", s, 1000);
        private String unit;
        private DateUnit supper;
        //跟上级单位转换的比率，比如ms->s，需要除以1000，则ms的scale=1000
        private int scale;

        DateUnit(String unit, DateUnit supper, int scale) {
            this.unit = unit;
            this.supper = supper;
            this.scale = scale;
        }

        @Override
        public String getUnit() {
            return unit;
        }

        @Override
        public DateUnit getSupper() {
            return supper;
        }

        @Override
        public int getScale() {
            return scale;
        }
    }

    private static String createDecimalFormatPattern(int precision) {
        if (precision >= 0 && precision <= 3) {
            return DECIMALFORMATPATTERNS[precision];
        }
        StringBuilder sb = new StringBuilder("#");
        for (int i = 1; i <= precision; i++) {
            if (i == 1) {
                sb.append(".");
            }
            sb.append("#");
        }
        return sb.toString();
    }
}
